/******************************************************************************
 * @file     dram_marchcx_test_runtime.c
 * @version  V0.10
 * $Revision: 1 $
 * $Date: 21/02/23 9:23a $
 * @brief    IEC60730 MarchC method for testing RAM
 * @note
 * SPDX-License-Identifier: Apache-2.0
 * Copyright (C) 2016-2020 Nuvoton Technology Corp. All rights reserved.
 ******************************************************************************/

#include <string.h>
#include "nuc980.h"
#include "IEC60730_CONTROL_PARAM.h"
#include "IEC60730_SAFETY_TEST_LIB.h"


#ifdef __ARMCC_VERSION
extern uint32_t Image$$RW_STACK$$Base;
extern uint32_t Image$$RW_STACK$$ZI$$Length;

extern uint32_t Image$$RW_RAM1$$Base;
//extern uint32_t Image$$RW_IRAM1_END$$Base;
#endif

#ifdef __ICCARM__
extern uint32_t CSTACK$$Base;
extern uint32_t CSTACK$$Limit;

static uint32_t RW_RAM1$$Base = 0x00300000;
//extern uint32_t RW_IRAM1_END$$Base;
#endif

extern uint32_t __initial_sp;
static uint32_t s_u32DramCurrentAddr, s_u32DramEndAddr;

extern uint32_t volatile u32DRAMStartAddr;
extern uint32_t volatile u32DRAMEndAddr;
extern uint32_t volatile g_u32RunTimeInit;

static uint32_t volatile u32IntSetting;

extern volatile uint32_t* g_pu32StackPtrn;

void Disable_AllException(void)
{
    asm volatile
    (
        "push    {r0-r7}  \n"
        "ldr     r1, [%0] \n"
        "mrs     r0, CPSR \n"
        "str     r0, [r1] \n"
        "and     r0, r0, #0xFFFFFF3F \n"
        "orr     r0, r0, #0x000000C0  \n"
        "msr     CPSR_c, r0 \n"
        "pop     {r0-r7} \n"
        "bx      lr  \n"
        :"=r"(u32IntSetting)
        :
        :
    );
}

void Enable_AllException(void)
{
    asm volatile
    (
        "push    {r0-r7} \n"
        "ldr     r1, [%0]\n"
        "ldr     r0, [r1] \n"
        "msr     CPSR_c, r0 \n"
        "mrs     r0, CPSR\n"
        "pop     {r0-r7}\n"
        "bx      lr\n"
        :
        :"r"  (u32IntSetting)
        :"memory"
    );
}

static void IEC60730_DRAM_MarchCX_Test_RunTime_Init(void)
{

#ifdef __ICCARM__
    s_u32DramCurrentAddr = (uint32_t) &RW_RAM1$$Base;
#endif

#ifdef __ARMCC_VERSION
    s_u32DramCurrentAddr = (uint32_t) &Image$$RW_RAM1$$Base;
#endif

    /* get stack pattern pointer and saved in g_pu32StackPtrn */
//  GetStackPtrnPtr();
    s_u32DramEndAddr = (uint32_t) g_pu32StackPtrn;
}

uint32_t static checkDMAAddr(uint32_t u32StartAddr, uint32_t u32EndAddr)
{
    uint32_t u32DMABaseAddr, u32DMALength;
    uint32_t u32return = 0;

    switch (inpw(REG_I2S_RESET)&(BIT5+BIT6))
    {
        case BIT5+BIT6: // playback & record
            /* check playback address */
            u32DMABaseAddr = inpw(REG_I2S_PDESB);
            u32DMABaseAddr &= ~BIT31;
            u32DMALength = inpw(REG_I2S_PDES_LENGTH);
            if ((u32DRAMStartAddr>=u32DMABaseAddr) && (u32DRAMEndAddr<(u32DMABaseAddr+u32DMALength)))
                break;
            /* check record address */
            u32DMABaseAddr = inpw(REG_I2S_RDESB);
            u32DMABaseAddr &= ~BIT31;
            u32DMALength = inpw(REG_I2S_RDES_LENGTH);
            if ((u32DRAMStartAddr>=u32DMABaseAddr) && (u32DRAMEndAddr<(u32DMABaseAddr+u32DMALength)))
                break;

            u32return = 1;
            break;
        case BIT5:
            /* check playback address */
            u32DMABaseAddr = inpw(REG_I2S_PDESB);
            u32DMABaseAddr &= ~BIT31;
            u32DMALength = inpw(REG_I2S_PDES_LENGTH);
            if ((u32DRAMStartAddr>=u32DMABaseAddr) && (u32DRAMEndAddr<(u32DMABaseAddr+u32DMALength)))
                break;
            u32return = 1;
            break;
        case BIT6:
            /* check record address */
            u32DMABaseAddr = inpw(REG_I2S_RDESB);
            u32DMABaseAddr &= ~BIT31;
            u32DMALength = inpw(REG_I2S_RDES_LENGTH);
            if ((u32DRAMStartAddr>=u32DMABaseAddr) && (u32DRAMEndAddr<(u32DMABaseAddr+u32DMALength)))
                break;
            u32return = 1;
            break;

        default:
            u32return = 1;
            break;
    }
    return u32return;
}

uint8_t IEC60730_DRAM_MarchC_Test_RunTime(void)
{
    uint8_t u8DRAMTestPass;
    uint8_t * pu8CopyToSafeArea;
    uint8_t *pu8AddrPtr;
    uint32_t u32StartAddr, u32EndAddr;
    uint32_t u32Length = DRAM_RUNTIME_TEST_LENGTH;

    if (!(g_u32RunTimeInit & RUNTIME_DRAM_INIT))
    {
        g_u32RunTimeInit |= RUNTIME_DRAM_INIT;
        IEC60730_DRAM_MarchCX_Test_RunTime_Init();
        u8DRAMTestPass = TEST_PASS;
    }

    u32StartAddr = s_u32DramCurrentAddr;
    u32EndAddr = s_u32DramEndAddr;
    pu8AddrPtr = (uint8_t *) u32StartAddr;

    /* disable interrupt for safe */
    Disable_AllException();

    /* if we need to backup ram data, safe ram reserved area is
       [0x3C004000-DRAM_RUNTIME_TEST_LENGTH*2  0x3C003FFF] */
    if (u32StartAddr == u32EndAddr - u32Length*2)
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length);
    else
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length*2);

    memcpy(pu8CopyToSafeArea, pu8AddrPtr, u32Length);

    /* start ram test */
    u32DRAMStartAddr = u32StartAddr;
    u32DRAMEndAddr =  u32DRAMStartAddr + u32Length;
    u32DRAMEndAddr -= 1;

    /* check if tested memory is not used by I2S */
#if 1
    if (checkDMAAddr(u32StartAddr, u32DRAMEndAddr))
        u8DRAMTestPass = IEC60730_PartDramMarchC_WOM();
#else
    u8DRAMTestPass = IEC60730_PartDramMarchC_WOM();
#endif

    /* restore the data */
    if(pu8CopyToSafeArea)
        memcpy(pu8AddrPtr, pu8CopyToSafeArea, u32Length);

    /* check if all ram have been tested (excluding safe reserved area) */
    u32StartAddr += u32Length;
    if (u32StartAddr >= u32EndAddr)
        g_u32RunTimeInit &= ~RUNTIME_DRAM_INIT;

    /* based on Artisan's SRAM architecture, for overlap test */
    u32StartAddr -= u32Length/2;
    s_u32DramCurrentAddr = u32StartAddr;

    /* enable interrupt */
    Enable_AllException();
    return u8DRAMTestPass;
}

uint8_t IEC60730_DRAM_MarchX_Test_RunTime(void)
{
    uint8_t u8DRAMTestPass;
    uint8_t * pu8CopyToSafeArea;
    uint8_t *pu8AddrPtr;
    uint32_t u32StartAddr, u32EndAddr;
    uint32_t u32Length = DRAM_RUNTIME_TEST_LENGTH;

    if (!(g_u32RunTimeInit & RUNTIME_DRAM_INIT))
    {
        g_u32RunTimeInit |= RUNTIME_DRAM_INIT;
        IEC60730_DRAM_MarchCX_Test_RunTime_Init();
        u8DRAMTestPass = TEST_PASS;
    }

    u32StartAddr = s_u32DramCurrentAddr;
    u32EndAddr = s_u32DramEndAddr;
    pu8AddrPtr = (uint8_t *) u32StartAddr;

    /* disable interrupt for safe */
    Disable_AllException();

    /* if we need to backup ram data, safe ram reserved area is
       [0x200008000-0x10*2  0x200007FFF] */
    if (u32StartAddr == u32EndAddr - u32Length*2)
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length);
    else
        pu8CopyToSafeArea= (uint8_t*) (u32EndAddr - u32Length*2);

    memcpy(pu8CopyToSafeArea, pu8AddrPtr, u32Length);

    /* start ram test */
    u32DRAMStartAddr = u32StartAddr;
    u32DRAMEndAddr =  u32DRAMStartAddr + u32Length;
    u32DRAMEndAddr -= 1;

    u8DRAMTestPass = IEC60730_PartDramMarchX_WOM();

    /* restore the data */
    if(pu8CopyToSafeArea)
        memcpy(pu8AddrPtr, pu8CopyToSafeArea, u32Length);

    /* check if all ram have been tested (excluding safe reserved area) */
    u32StartAddr += u32Length;
    if (u32StartAddr >= u32EndAddr)
        g_u32RunTimeInit &= ~RUNTIME_DRAM_INIT;

    /* based on Artisan's SRAM architecture, for overlap test */
    u32StartAddr -= u32Length/2;
    s_u32DramCurrentAddr = u32StartAddr;

    /* enable interrupt */
    Enable_AllException();
    return u8DRAMTestPass;
}

